在前面幾節,我們已經完成:
下一步就來處理實務上最常見的需求:「角色與權限管理」
例如:
使用者角色 | 能進的頁面 | 能做的操作 |
---|---|---|
admin | 所有頁面 | 刪除、修改、審核 |
editor | /dashboard, /posts | 編輯但不能刪除 |
user | /profile | 只能看自己資料 |
如果你打算做中大型專案,那麼用 Redux 來集中管理 Auth 狀態與權限會讓架構更清楚。
功能 | AuthContext | Redux Toolkit |
---|---|---|
小專案好上手 | ✅ | ⛔ |
全域共享狀態 | ✅ | ✅ |
可拆 slice / middleware | ⛔ | ✅ |
可支援角色、權限、loading 狀態等複雜管理 | ⛔ | ✅ ✅ ✅ |
DevTools 可追蹤狀態變化 | ⛔ | ✅ |
適合大型專案 | ❌ | ✅ ✅ ✅ |
建立 authSlice
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { loginAndSetCookie, logout, getProtectedData } from "../utils/auth";
// 異步登入
export const loginThunk = createAsyncThunk(
"auth/login",
async ({ email, password }) => {
const data = await loginAndSetCookie(email, password);
return data; // { uid, email }
}
);
// 驗證當前使用者(從 Cookie)
export const fetchCurrentUser = createAsyncThunk(
"auth/me",
async () => {
const data = await getProtectedData(); // 後端從 Cookie 驗證
return data.user; // { uid, email, role }
}
);
const authSlice = createSlice({
name: "auth",
initialState: {
user: null,
role: null, // admin / editor / user ...
loading: false,
},
reducers: {
clearAuth: (state) => {
state.user = null;
state.role = null;
},
},
extraReducers: (builder) => {
builder
.addCase(loginThunk.pending, (state) => {
state.loading = true;
})
.addCase(loginThunk.fulfilled, (state, action) => {
state.loading = false;
state.user = action.payload;
state.role = action.payload.role || "user";
})
.addCase(fetchCurrentUser.fulfilled, (state, action) => {
state.user = action.payload;
state.role = action.payload.role || "user";
});
},
});
export const { clearAuth } = authSlice.actions;
export default authSlice.reducer;
建立 Redux Store
import { configureStore } from "@reduxjs/toolkit";
import authReducer from "./authSlice";
export const store = configureStore({
reducer: {
auth: authReducer,
},
});
在 App 最外層包住 Provider
import { Provider } from "react-redux";
import { store } from "./store";
ReactDOM.createRoot(document.getElementById("root")).render(
<Provider store={store}>
<AppRoutes />
</Provider>
);
ProtectedRoute 加上「角色判斷」
import { Navigate } from "react-router-dom";
import { useSelector } from "react-redux";
export default function ProtectedRoute({ children, roles = [] }) {
const { user, role, loading } = useSelector((state) => state.auth);
if (loading) return <p>驗證中...</p>;
if (!user) return <Navigate to="/login" replace />;
if (roles.length > 0 && !roles.includes(role)) {
return <p>你沒有權限(需要: {roles.join(", ")})</p>;
}
return children;
}
配置不同權限的路由
<Route
path="/admin"
element={
<ProtectedRoute roles={["admin"]}>
<AdminPanel />
</ProtectedRoute>
}
/>
<Route
path="/editor"
element={
<ProtectedRoute roles={["admin", "editor"]}>
<EditorPage />
</ProtectedRoute>
}
/>